package position

import (
	b "code.google.com/p/gochess/board"
	"fmt"
)

type Color uint8

// The piece colors. Order does matter, do not change.
//
// NoColor is the zero value for Color.
// Border is the (virtual) color of the BorderPieces.
const (
	NoColor Color = iota
	White
	Black
	Border

	// BiggestColor represents the upper limit of Color values.
	// As "Border" is an artificial color it is ignored.
	BiggestColor Color = Black
)

const (
	bitSizeColor = 2
	bitMaskColor = 1<<bitSizeColor - 1
)

var colors = [...]string{
	NoColor: "NoColor", White: "White", Black: "Black", Border: "Border"}

func (this Color) String() string {
	return colors[this]
}

// PawnRank returns the initial rank of the pawns for the given color.
func PawnRank(color Color) b.Rank {
	return b.Rank2 + b.Rank(color-1)*5
}

// PromotionRank returns for the given color the rank from where
// a pawn is promoted in the next move.
func PromotionRank(color Color) b.Rank {
	return b.Rank7 - b.Rank(color-1)*5
}

// Opponent returns the color of the opponent. Opponents are
// 		White <-> Black
// 		NoColor <-> Border
func Opponent(color Color) Color {
	return 0x03 ^ color
}

// MoveDirection returns the move direction of the pawns for the given color.
func MoveDirection(color Color) b.Square {
	return b.Up + (b.Square(color-1)*b.Down)<<1
}

// PieceType represents the type of a piece.
type PieceType uint8

// The piece types. Order does matter, do not change.
//
// NoPieceType is the zero value for PieceType.
const (
	NoPieceType PieceType = iota
	Pawn
	Knight
	Bishop
	Rook
	Queen
	King

	// BiggestPieceType is the upper limit of all piece types.
	BiggestPieceType PieceType = King
)

const (
	BitSizePieceType = 3
	BitMaskPieceType = 1<<BitSizePieceType - 1
)

var pieceTypes = map[PieceType]string{
	NoPieceType: "NoPieceType", King: "K", Queen: "Q", Rook: "R", Bishop: "B", Knight: "N", Pawn: "P",
}

func (this PieceType) String() string {
	return pieceTypes[this]
}

// this is a piece (see it as an instance) on the board
type Piece uint8

// NoPiece is the zero value for Piece.
const NoPiece Piece = 0x00

const (
	bitSizePiece = bitSizeColor + BitSizePieceType
	bitMaskPiece = 1<<bitSizePiece - 1
)

// BiggestPiece marks the upper limit for pieces in a way that
// for every valid piece of type Piece, except BorderPiece, the equation
//		piece <= BiggestPiece
// is true.
const BiggestPiece Piece = Piece(BiggestPieceType) | Piece(BiggestColor<<BitSizePieceType)

// NewPiece returns a chess piece with the given color and piece type.
func NewPiece(player Color, piece PieceType) Piece {
	return Piece(piece) | Piece(player)<<BitSizePieceType
}

// Color returns the color of the piece.
func (p Piece) Color() Color {
	return Color(p >> BitSizePieceType)
}

// Type returns the type of the piece.
func (p Piece) Type() PieceType {
	return PieceType(p & BitMaskPieceType)
}

// Pack shrinks a piece.
func (p Piece) Pack() Piece {
	return p - 1<<BitSizePieceType
}

// Pack unshrinks a piece.
func (p Piece) Unpack() Piece {
	return p + 1<<BitSizePieceType
}

const BiggestPackedPiece Piece = Piece(BiggestPieceType) |
	Piece(BiggestColor<<BitSizePieceType) - 1<<BitSizePieceType

// Inc returns the next (in size) piece. Useful for loops.
func (p Piece) Inc() Piece {
	return p + 1
}

var colorsShort = [...]string{
	NoColor: "NoColor", White: "w", Black: "b", Border: "Border",
}

func (this Piece) String() string {

	switch this {
	case NoPiece:
		return "NoPiece"
	case BorderPiece:
		return "BorderPiece"
	default:
		return fmt.Sprintf("%s%v", colorsShort[this.Color()], this.Type())
	}
}

// These are handy shortcuts for pieces.
var (
	WhitePawn   = NewPiece(White, Pawn)
	WhiteKnight = NewPiece(White, Knight)
	WhiteBishop = NewPiece(White, Bishop)
	WhiteRook   = NewPiece(White, Rook)
	WhiteQueen  = NewPiece(White, Queen)
	WhiteKing   = NewPiece(White, King)

	BlackPawn   = NewPiece(Black, Pawn)
	BlackKnight = NewPiece(Black, Knight)
	BlackBishop = NewPiece(Black, Bishop)
	BlackRook   = NewPiece(Black, Rook)
	BlackQueen  = NewPiece(Black, Queen)
	BlackKing   = NewPiece(Black, King)

	// BorderPiece is a piece which is put on squares which belong to
	// the border of the virtual board.
	// They are a means to find out if a square is valid.
	BorderPiece = NewPiece(Border, NoPieceType)
)

func init() {

	// make sure that all pieces are <= BiggestPiece
	max := NoPiece
	for _, color := range []Color{NoColor, White, Black} {
		for _, pt := range []PieceType{King, Queen, Rook, Bishop, Knight, Pawn, NoPieceType} {
			piece := NewPiece(color, pt)
			if piece > BiggestPiece {
				panic(fmt.Sprintf("color %v, piece type %v: piece %v has int representation %d, which is > BiggestPiece %d",
					color, pt, piece, piece, BiggestPiece))
			}
			if piece > max {
				max = piece
			}
		}
	}
	// ensure an optimal (=minimal) BiggestPiece value
	if max < BiggestPiece {
		panic(fmt.Sprintf("maximum of all piece int representations is %d (%v), which is < BiggestPiece %d. BiggestPiece can be defined to be lower! Do this!",
			max, Piece(max), BiggestPiece))
	}
}
